package com.dtzhejiang.social.supervision.risk.engine.core.task.tasklet;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.dtzhejiang.social.supervision.risk.engine.common.domain.bo.SpiderHospitalLicenseMatchBO;
import com.dtzhejiang.social.supervision.risk.engine.common.domain.po.*;
import com.dtzhejiang.social.supervision.risk.engine.common.util.AlgorithmUtils;
import com.dtzhejiang.social.supervision.risk.engine.common.util.LatitudeUtils;
import com.dtzhejiang.social.supervision.risk.engine.common.util.PageDataUtil;
import com.dtzhejiang.social.supervision.risk.engine.core.mapper.SpiderHospitalMapper;
import com.dtzhejiang.social.supervision.risk.engine.core.mapper.SuperviseObjectInfoMapper;
import com.dtzhejiang.social.supervision.risk.engine.core.service.LicenseService;
import com.dtzhejiang.social.supervision.risk.engine.core.mapper.LicenceInfoMapper;
import com.dtzhejiang.social.supervision.risk.engine.core.mapper.SpiderHospitalLicenseMatchMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: chenwenhao
 * Date: 9:25 2020/11/18
 * Content:店铺证照信息匹配
 */
@Component
@Slf4j
public class LicenseMatchTask {

    @Resource
    private LicenseService licenseService;
    @Resource
    private LicenceInfoMapper licenceInfoMapper;

    @Resource
    private SpiderHospitalLicenseMatchMapper spiderHospitalLicenseMatchMapper;

    @Resource
    private SuperviseObjectInfoMapper superviseObjectInfoMapper;

    @Resource
    private SpiderHospitalMapper spiderHospitalMapper;

    private static final String INSTITUTION_NAME_SUFFIX = "有限公司";

    private static final String HE_HUO_QI_YE_NORMAL = "合伙企业(普通合伙)";

    private static final String HE_HUO_QI_YE = "(普通合伙)";

    private static final String DELIMITER = ";";

    /**
     * 统一社会信用代码和执业许可证登记号匹配成功的阈值
     */
    private static final BigDecimal CODE_COMPARE_THRESHOLD = BigDecimal.valueOf(0.8);

    /**
     * 存放主体和纬度对应信息
     */
    private static Map<String, BigDecimal> LATITUDE_MAP = new HashMap<>();

    /**
     * 存放主体和经度对应信息
     */
    private static Map<String, BigDecimal> LONGITUDE_MAP = new HashMap<>();

    /**
     * 存放店铺区域对应信息
     */
    private static Map<String, String> REGION_BY_SHOP_ID = new HashMap<>();

    /**
     * 证照信息分区域存放
     */
    private static Map<String, List<LicenceInfoEntity>> LICENCE_BY_REGION = new HashMap<>();

    /**
     * 存放统一社会信用代码和证照信息的对应
     */
    private static Map<String, LicenceInfoEntity> LICENCE_BY_UNI_CODE = new HashMap<>();
    /**
     * 存放登记号和证照信息的对应关系
     */
    private static Map<String, LicenceInfoEntity> LICENCE_BY_REG_NO = new HashMap<>();

    /**
     * 批量插入的最大值
     */
    private static final int INSERT_BATCH_SIZE = 500;

    private static final int SELECT_MAX_SIZE = 10000;



    /**
     * 匹配任务执行时间
     */
    private static final String MATCH_TIME = "0 0 2 * * ?";

    @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
    public void matchLicense() {
        try {
            List<String> matchedShopIdList = spiderHospitalLicenseMatchMapper.selectAllShopId();
            //获取所有解析的营业执照信息
            List<SpiderHospitalBusinessLicenseEntity> businessLicenseList = licenseService.getAllBusinessLicense();
            Map<String, SpiderHospitalBusinessLicenseEntity> businessLicenseMap = new HashMap<>(256);
            //获取所有解析的执业许可证信息
            List<SpiderHospitalPracticeLicenseEntity> practiceLicenseList = licenseService.getAllPracticeLicense();
            Map<String, SpiderHospitalPracticeLicenseEntity> practiceLicenseMap = new HashMap<>(256);
            //存放所有需要匹配的店铺id
            Set<String> shopIdSet = new HashSet<>();
            if (CollectionUtils.isNotEmpty(businessLicenseList)) {
                shopIdSet.addAll(businessLicenseList.stream().map(SpiderHospitalBusinessLicenseEntity::getShopId).collect(Collectors.toSet()));
                businessLicenseList.stream().forEach(businessLicense -> {
                    businessLicenseMap.put(businessLicense.getShopId(), businessLicense);
                });
            }
            if (CollectionUtils.isNotEmpty(practiceLicenseList)) {
                shopIdSet.addAll(practiceLicenseList.stream().map(SpiderHospitalPracticeLicenseEntity::getShopId).collect(Collectors.toSet()));
                practiceLicenseList.stream().forEach(practiceLicense -> {
                    practiceLicenseMap.put(practiceLicense.getShopId(), practiceLicense);
                });
            }
            //过滤掉已经匹配过的店铺
            shopIdSet.removeAll(matchedShopIdList);
            //如果店铺id集合为空，说明没有需要匹配的证照信息，直接结束
            if (CollectionUtils.isEmpty(shopIdSet)) {
                return;
            }
            this.initLatitudeAndLicense();
            //如果odps推送过来的营业执照和执业许可证信息为空，则无法匹配，直接停止
            if (MapUtils.isEmpty(LICENCE_BY_REGION)) {
                return;
            }
            this.initShopRegion(shopIdSet);
            List<SpiderHospitalLicenseMatchEntity> licenseMatchList = getLicenseMatchResult(shopIdSet, businessLicenseMap, practiceLicenseMap);
            if (CollectionUtils.isNotEmpty(licenseMatchList)) {
                //分批保存匹配结果
                this.saveLicenseMatchRecord(licenseMatchList);
            }
        } catch (Exception e) {
            log.error("证照匹配任务发生异常！", e);
        } finally {
            REGION_BY_SHOP_ID.clear();
            LONGITUDE_MAP.clear();
            LATITUDE_MAP.clear();
            LICENCE_BY_REGION.clear();
            LICENCE_BY_UNI_CODE.clear();
            LICENCE_BY_REG_NO.clear();
        }
    }

    /**
     * 存储店铺区域信息
     * @param shopIds
     */
    private void initShopRegion(Collection<String> shopIds) {
        QueryWrapper<SpiderHospitalEntity> wrapper = new QueryWrapper<>();
        wrapper.select("shop_id, city").in("shop_id", shopIds);
        List<SpiderHospitalEntity> spiderHospitalEntities = spiderHospitalMapper.selectList(wrapper);
        if (CollectionUtils.isNotEmpty(spiderHospitalEntities)) {
            spiderHospitalEntities.stream().forEach(spiderHospitalEntity -> {
                REGION_BY_SHOP_ID.put(spiderHospitalEntity.getShopId(), spiderHospitalEntity.getCity());
            });
            spiderHospitalEntities.clear();
        }
    }

    /**
     * 存储经纬度信息，以及证照信息
     */
    private void initLatitudeAndLicense() {
        QueryWrapper<SuperviseObjectInfoEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("pripid, latitude, longitude,region_name");
        long total = superviseObjectInfoMapper.countAll();
        List<SuperviseObjectInfoEntity> superviseObjectInfoEntities = new ArrayList<>();
        for(long i = 1; (i-1)*SELECT_MAX_SIZE < total; i++ ){
            IPage<SuperviseObjectInfoEntity> iPage = superviseObjectInfoMapper.selectPage(PageDataUtil.buildPageParam(i, SELECT_MAX_SIZE),queryWrapper);
            superviseObjectInfoEntities.addAll(iPage.getRecords());
        }
        Map<String, String> regionByPriPid = new HashMap<>(65536);
        if (CollectionUtils.isNotEmpty(superviseObjectInfoEntities)) {
            for (SuperviseObjectInfoEntity superviseObjectInfoEntity : superviseObjectInfoEntities) {
                if (superviseObjectInfoEntity.getLatitude() != null && superviseObjectInfoEntity.getLongitude() != null) {
                    LATITUDE_MAP.put(superviseObjectInfoEntity.getPripid(), superviseObjectInfoEntity.getLatitude());
                    LONGITUDE_MAP.put(superviseObjectInfoEntity.getPripid(), superviseObjectInfoEntity.getLatitude());
                }
                regionByPriPid.put(superviseObjectInfoEntity.getPripid(), superviseObjectInfoEntity.getRegionName());
            }
            superviseObjectInfoEntities.clear();
        }
        this.initLicense(regionByPriPid);
    }

    /**
     * 将证照信息分城市存储
     * @param regionByPriPid
     */
    private void initLicense(Map<String, String> regionByPriPid) {
        QueryWrapper<LicenceInfoEntity> wrapper = new QueryWrapper<>();
        wrapper.select("object_name,alias_name,pripid,uni_code,doc_no,address,business_object_name,business_address")
                .eq("delete_flag", 0);
        List<LicenceInfoEntity> licenceInfoEntities = new ArrayList<>();
        Integer total = licenceInfoMapper.selectCount(null);
        for(int i = 1; (i-1)*SELECT_MAX_SIZE < total; i++ ){
            IPage<LicenceInfoEntity> iPage = licenceInfoMapper.selectPage(PageDataUtil.buildPageParam(i, SELECT_MAX_SIZE),wrapper);
            licenceInfoEntities.addAll(iPage.getRecords());
        }
        if (CollectionUtils.isNotEmpty(licenceInfoEntities)) {
            for (LicenceInfoEntity licenceInfoEntity : licenceInfoEntities) {
                String regionName = regionByPriPid.get(licenceInfoEntity.getPripid());
                //取区域中的城市名
                if (StringUtils.isNotBlank(regionName) && regionName.length() > 2) {
                    regionName = regionName.substring(0, 2);
                } else {
                    regionName = "";
                }
                if (StringUtils.isNotBlank(licenceInfoEntity.getUniCode())) {
                    LICENCE_BY_UNI_CODE.put(licenceInfoEntity.getUniCode(), licenceInfoEntity);
                }
                if (StringUtils.isNotBlank(licenceInfoEntity.getDocNo())) {
                    LICENCE_BY_REG_NO.put(licenceInfoEntity.getDocNo(), licenceInfoEntity);
                }
                LICENCE_BY_REGION.computeIfAbsent(regionName, k -> new ArrayList<LicenceInfoEntity>());
                LICENCE_BY_REGION.get(regionName).add(licenceInfoEntity);
            }
            licenceInfoEntities.clear();
        }

    }

    /**
     * 分批插入匹配结果
     * @param licenseMatchList
     */
    private void saveLicenseMatchRecord(List<SpiderHospitalLicenseMatchEntity> licenseMatchList) {
        int start = 0;
        int end = 0;
        int size = licenseMatchList.size();
        List<SpiderHospitalLicenseMatchEntity> tempLicenseMatchList;
        while (size > end) {
            end += INSERT_BATCH_SIZE;
            tempLicenseMatchList = licenseMatchList.subList(start, Math.min(size, end));
            spiderHospitalLicenseMatchMapper.batchInsert(tempLicenseMatchList);
            start = end;
        }
    }

    /**
     * 过滤匹配结果，如果营业执照和执业许可证匹配的主体不一致，需要取匹配度较高的主体为匹配主体
     * @param shopIdSet
     * @param businessLicenseMap
     * @param practiceLicenseMap
     * @return
     */
    private static List<SpiderHospitalLicenseMatchEntity> getLicenseMatchResult(Set<String> shopIdSet, Map<String, SpiderHospitalBusinessLicenseEntity> businessLicenseMap,
                                                                   Map<String, SpiderHospitalPracticeLicenseEntity> practiceLicenseMap) {
        List<LicenceInfoEntity> licenceInfoEntities = new ArrayList<>();
        Set<String> regionSet = LICENCE_BY_REGION.keySet();
        for (String region: regionSet) {
            licenceInfoEntities.addAll(LICENCE_BY_REGION.get(region));
        }
        List<SpiderHospitalLicenseMatchEntity> licenseMatchList = new ArrayList<>();
        List<SpiderHospitalLicenseMatchBO> licenseMatchTempList = getLicenseMatchResultTemp(shopIdSet, businessLicenseMap, practiceLicenseMap, licenceInfoEntities);
        if (CollectionUtils.isNotEmpty(licenseMatchTempList)) {
            Map<String, LicenceInfoEntity> licenceInfoMap = new HashMap<>(65536);

            licenceInfoEntities.stream().forEach(licenceInfoEntity -> {
                licenceInfoMap.put(licenceInfoEntity.getPripid(), licenceInfoEntity);
            });
            for (SpiderHospitalLicenseMatchBO licenseMatchTemp: licenseMatchTempList) {
                SpiderHospitalLicenseMatchEntity licenseMatch = new SpiderHospitalLicenseMatchEntity();
                licenseMatchList.add(licenseMatch);
                //如果营业执照和执业许可证都没匹配上主体，则继续下一个循环
                if (StringUtils.isBlank(licenseMatchTemp.getBusinessPriPid()) && StringUtils.isBlank(licenseMatchTemp.getPracticePriPid())) {
                    BeanUtils.copyProperties(licenseMatchTemp, licenseMatch);
                    continue;
                }
                //走到这一步说明营业执照和执业许可证都匹配到了主体，需要判断两个证照匹配的主体是否一致，如果不一致，以匹配度高的为准
                if (StringUtils.isNotBlank(licenseMatchTemp.getBusinessPriPid()) && StringUtils.isNotBlank(licenseMatchTemp.getPracticePriPid())
                        && licenseMatchTemp.getBusinessPriPid().equals(licenseMatchTemp.getPracticePriPid())) {
                    BeanUtils.copyProperties(licenseMatchTemp, licenseMatch);
                    licenseMatch.setPripid(licenseMatchTemp.getPracticePriPid());
                } else {
                    getHighMatchResult(licenseMatchTemp, licenseMatch, licenceInfoMap, businessLicenseMap, practiceLicenseMap);
                }
            }
        }
        return licenseMatchList;
    }

    /**
     * 比较营业执照和执业许可证的匹配度，取较高匹配度的主体
     * @param licenseMatch
     * @param licenceInfoMap
     */
    private static void getHighMatchResult(SpiderHospitalLicenseMatchBO licenseMatchTemp, SpiderHospitalLicenseMatchEntity licenseMatch,
                                    Map<String, LicenceInfoEntity> licenceInfoMap, Map<String, SpiderHospitalBusinessLicenseEntity> businessLicenseMap,
                                    Map<String, SpiderHospitalPracticeLicenseEntity> practiceLicenseMap) {
        licenseMatch.setShopId(licenseMatchTemp.getShopId());
        //如果营业执照的匹配度大于等于执业许可证的匹配度，则使用营业执照匹配的结果，否则使用执业许可证匹配的结果
        if (licenseMatchTemp.getUniCodeDegree().compareTo(licenseMatchTemp.getRegNoDegree()) >= 0) {
            licenseMatch.setPripid(licenseMatchTemp.getBusinessPriPid());
            licenseMatch.setUniCodeDegree(licenseMatchTemp.getUniCodeDegree());
            licenseMatch.setBusinessNameDegree(licenseMatchTemp.getBusinessNameDegree());
            licenseMatch.setBusinessAddressDegree(licenseMatchTemp.getBusinessAddressDegree());
            SpiderHospitalPracticeLicenseEntity practiceLicense = practiceLicenseMap.get(licenseMatchTemp.getShopId());
            //如果存在执业许可证信息
            if (practiceLicense != null) {
                //获取主体信息
                LicenceInfoEntity licenceInfoEntity = licenceInfoMap.get(licenseMatchTemp.getBusinessPriPid());
                licenseMatch.setRegNoDegree(AlgorithmUtils.getSimilarityByLevenshteinDistance(practiceLicense.getRegisterNo(), licenceInfoEntity.getDocNo()));
                licenseMatch.setPracticeNameDegree(institutionNameMatch(practiceLicense.getInstitutionName(), licenceInfoEntity.getObjectName(), licenceInfoEntity.getAliasName(), licenceInfoEntity.getBusinessObjectName()));
                licenseMatch.setPracticeAddressDegree(getMaxInstitutionAddressDegree(practiceLicense.getAddress(), licenceInfoEntity.getAddress(), licenceInfoEntity.getBusinessAddress(),licenceInfoEntity.getPripid(), practiceLicense.getLatitude(), practiceLicense.getLongitude()));
            }
        } else {
            licenseMatch.setPripid(licenseMatchTemp.getPracticePriPid());
            licenseMatch.setRegNoDegree(licenseMatchTemp.getRegNoDegree());
            licenseMatch.setPracticeNameDegree(licenseMatchTemp.getPracticeNameDegree());
            licenseMatch.setPracticeAddressDegree(licenseMatchTemp.getPracticeAddressDegree());
            SpiderHospitalBusinessLicenseEntity businessLicense = businessLicenseMap.get(licenseMatchTemp.getShopId());
            //如果存在营业执照信息
            if (businessLicense != null) {
                //获取主体信息
                LicenceInfoEntity licenceInfoEntity = licenceInfoMap.get(licenseMatchTemp.getPracticePriPid());
                licenseMatch.setUniCodeDegree(AlgorithmUtils.getSimilarityByLevenshteinDistance(businessLicense.getUniCode(), licenceInfoEntity.getUniCode()));
                licenseMatch.setBusinessNameDegree(institutionNameMatch(businessLicense.getInstitutionName(), licenceInfoEntity.getObjectName(), licenceInfoEntity.getAliasName(), licenceInfoEntity.getBusinessObjectName()));
                licenseMatch.setBusinessAddressDegree(getMaxInstitutionAddressDegree(businessLicense.getAddress(), licenceInfoEntity.getAddress(), licenceInfoEntity.getBusinessAddress(), licenceInfoEntity.getPripid(), businessLicense.getLatitude(), businessLicense.getLongitude()));
            }
        }
    }

    /**
     * 获取匹配结果中间值，此时营业执照匹配到的主体和执业许可证匹配到的主体可能不一致
     * @param shopIdSet
     * @param businessLicenseMap
     * @param practiceLicenseMap
     * @param licenceInfoEntities
     * @return
     */
    private static List<SpiderHospitalLicenseMatchBO> getLicenseMatchResultTemp(Set<String> shopIdSet, Map<String, SpiderHospitalBusinessLicenseEntity> businessLicenseMap,
                                                                       Map<String, SpiderHospitalPracticeLicenseEntity> practiceLicenseMap,
                                                                       List<LicenceInfoEntity> licenceInfoEntities) {
        List<SpiderHospitalLicenseMatchBO> licenseMatchTempList = new ArrayList<>();
        for (String shopId : shopIdSet) {
            SpiderHospitalLicenseMatchBO licenseMatchTemp = new SpiderHospitalLicenseMatchBO();
            licenseMatchTemp.setShopId(shopId);
            licenseMatchTempList.add(licenseMatchTemp);
            SpiderHospitalBusinessLicenseEntity businessLicense = businessLicenseMap.get(shopId);
            SpiderHospitalPracticeLicenseEntity practiceLicense = practiceLicenseMap.get(shopId);
            LicenceInfoEntity licenceInfo = null;
            //判断统一社会信用代码是否完全匹配
            if (businessLicense != null && LICENCE_BY_UNI_CODE.get(businessLicense.getUniCode()) != null) {
                //取出完全匹配的证照信息，进行匹配
                licenceInfo = LICENCE_BY_UNI_CODE.get(businessLicense.getUniCode());
            }
            //判断执业许可证登记号是否完全匹配
            if (practiceLicense != null && LICENCE_BY_REG_NO.get(practiceLicense.getRegisterNo()) != null) {
                //取出完全匹配的证照信息，进行匹配
                licenceInfo = LICENCE_BY_REG_NO.get(practiceLicense.getRegisterNo());
            }
            //如果存在完全匹配的证照信息，直接计算匹配度即可，后续流程就不需要了
            if (licenceInfo != null) {
                exactMatch(licenceInfo, businessLicense, practiceLicense, licenseMatchTemp);
                continue;
            }
            String uniCode = businessLicense == null ? "" : businessLicense.getUniCode();
            String regNo = practiceLicense == null ? "" : practiceLicense.getRegisterNo();
            //如果统一社会信用代码和登记号长度都过短，认为ocr识别失败，统一社会信用代码匹配度置为-1
            if (uniCode.length() < 12 && regNo.length() < 16) {
                licenseMatchTemp.setUniCodeDegree(BigDecimal.valueOf(-1));
                continue;
            }
            String regionName = REGION_BY_SHOP_ID.get(shopId);
            if (StringUtils.isNotBlank(regionName)) {
                if (CollectionUtils.isNotEmpty(LICENCE_BY_REGION.get(regionName))) {
                    licenceInfoEntities = LICENCE_BY_REGION.get(regionName);
                }
            }
            //循环所有的证照信息，找出匹配度最高的主体
            for (LicenceInfoEntity licenceInfoEntity : licenceInfoEntities) {
                //如果统一社会信用代码和执业许可证登记号都为空，则无法匹配，跳到下一个循环
                if (StringUtils.isBlank(licenceInfoEntity.getUniCode()) && StringUtils.isBlank(licenceInfoEntity.getDocNo())) {
                    continue;
                }
                //如果ocr解析的营业执照信息不为空，并且odps推送的数据的统一社会信用代码也不为空，则进行营业执照匹配
                if (businessLicense != null && StringUtils.isNotBlank(licenceInfoEntity.getUniCode())) {
                    BigDecimal uniCodeDegree = AlgorithmUtils.getSimilarityByLevenshteinDistance(businessLicense.getUniCode(), licenceInfoEntity.getUniCode());
                    //如果统一信息用代码匹配度大于0，则进行名称和地址匹配，否则认为无法匹配
                    if (uniCodeDegree.compareTo(CODE_COMPARE_THRESHOLD) >= 0) {
                        BigDecimal businessNameDegree = institutionNameMatch(businessLicense.getInstitutionName(), licenceInfoEntity.getObjectName(), licenceInfoEntity.getAliasName(), licenceInfoEntity.getBusinessObjectName());
                        BigDecimal businessAddressDegree = getMaxInstitutionAddressDegree(businessLicense.getAddress(), licenceInfoEntity.getAddress(), licenceInfoEntity.getBusinessAddress(), licenceInfoEntity.getPripid(), businessLicense.getLatitude(), businessLicense.getLongitude());
                        //如果新匹配主体的匹配度总和大于老的，则更新匹配度
                        if (uniCodeDegree.compareTo(licenseMatchTemp.getUniCodeDegree()) > 0) {
                            licenseMatchTemp.setBusinessPriPid(licenceInfoEntity.getPripid());
                            licenseMatchTemp.setUniCodeDegree(uniCodeDegree);
                            licenseMatchTemp.setBusinessNameDegree(businessNameDegree);
                            licenseMatchTemp.setBusinessAddressDegree(businessAddressDegree);
                        } else if (uniCodeDegree.compareTo(licenseMatchTemp.getUniCodeDegree()) == 0) {
                            if (businessNameDegree.compareTo(licenseMatchTemp.getBusinessNameDegree()) > 0) {
                                licenseMatchTemp.setBusinessPriPid(licenceInfoEntity.getPripid());
                                licenseMatchTemp.setUniCodeDegree(uniCodeDegree);
                                licenseMatchTemp.setBusinessNameDegree(businessNameDegree);
                                licenseMatchTemp.setBusinessAddressDegree(businessAddressDegree);
                            } else if (businessNameDegree.compareTo(licenseMatchTemp.getBusinessNameDegree()) == 0) {
                                if (businessAddressDegree.compareTo(licenseMatchTemp.getBusinessAddressDegree()) > 0) {
                                    licenseMatchTemp.setBusinessPriPid(licenceInfoEntity.getPripid());
                                    licenseMatchTemp.setUniCodeDegree(uniCodeDegree);
                                    licenseMatchTemp.setBusinessNameDegree(businessNameDegree);
                                    licenseMatchTemp.setBusinessAddressDegree(businessAddressDegree);
                                }
                            }
                        }
                    }
                }
                //如果ocr解析的执业许可证信息不为空，并且odps推送的数据的登记号也不为空，则进行执业许可证匹配
                if (practiceLicense != null && StringUtils.isNotBlank(licenceInfoEntity.getDocNo())) {
                    BigDecimal regNoDegree = AlgorithmUtils.getSimilarityByLevenshteinDistance(practiceLicense.getRegisterNo(), licenceInfoEntity.getDocNo());
                    //如果执业许可证登记号匹配度大于0，则进行名称和地址匹配，否则认为无法匹配
                    if (regNoDegree.compareTo(CODE_COMPARE_THRESHOLD) >= 0) {
                        BigDecimal practiceNameDegree = institutionNameMatch(practiceLicense.getInstitutionName(), licenceInfoEntity.getObjectName(), licenceInfoEntity.getAliasName(), licenceInfoEntity.getBusinessObjectName());
                        BigDecimal practiceAddressDegree = getMaxInstitutionAddressDegree(practiceLicense.getAddress(), licenceInfoEntity.getAddress(), licenceInfoEntity.getBusinessAddress(), licenceInfoEntity.getPripid(), practiceLicense.getLatitude(), practiceLicense.getLongitude());
                        //如果新匹配主体的匹配度总和大于老的，则更新匹配度
                        if (regNoDegree.compareTo(licenseMatchTemp.getRegNoDegree()) > 0) {
                            licenseMatchTemp.setPracticePriPid(licenceInfoEntity.getPripid());
                            licenseMatchTemp.setRegNoDegree(regNoDegree);
                            licenseMatchTemp.setPracticeNameDegree(practiceNameDegree);
                            licenseMatchTemp.setPracticeAddressDegree(practiceAddressDegree);
                        } else if (regNoDegree.compareTo(licenseMatchTemp.getRegNoDegree()) == 0) {
                            if (practiceNameDegree.compareTo(licenseMatchTemp.getPracticeNameDegree()) > 0) {
                                licenseMatchTemp.setPracticePriPid(licenceInfoEntity.getPripid());
                                licenseMatchTemp.setRegNoDegree(regNoDegree);
                                licenseMatchTemp.setPracticeNameDegree(practiceNameDegree);
                                licenseMatchTemp.setPracticeAddressDegree(practiceAddressDegree);
                            } else if (practiceNameDegree.compareTo(licenseMatchTemp.getPracticeNameDegree()) == 0) {
                                if (practiceAddressDegree.compareTo(licenseMatchTemp.getPracticeAddressDegree()) > 0) {
                                    licenseMatchTemp.setPracticePriPid(licenceInfoEntity.getPripid());
                                    licenseMatchTemp.setRegNoDegree(regNoDegree);
                                    licenseMatchTemp.setPracticeNameDegree(practiceNameDegree);
                                    licenseMatchTemp.setPracticeAddressDegree(practiceAddressDegree);
                                }
                            }
                        }
                    }
                }
            }
        }
        return licenseMatchTempList;
    }

    /**
     * 证照完全匹配时，进行匹配杜计算
     * @param licenceInfoEntity
     * @param businessLicense
     * @param practiceLicense
     * @param licenseMatchTemp
     */
    private static void exactMatch(LicenceInfoEntity licenceInfoEntity, SpiderHospitalBusinessLicenseEntity businessLicense,
                            SpiderHospitalPracticeLicenseEntity practiceLicense, SpiderHospitalLicenseMatchBO licenseMatchTemp) {
        licenseMatchTemp.setBusinessPriPid(licenceInfoEntity.getPripid());
        licenseMatchTemp.setPracticePriPid(licenceInfoEntity.getPripid());
        if (businessLicense != null) {
            if (StringUtils.isNotBlank(licenceInfoEntity.getUniCode())) {
                licenseMatchTemp.setUniCodeDegree(AlgorithmUtils.getSimilarityByLevenshteinDistance(businessLicense.getUniCode(), licenceInfoEntity.getUniCode()));
            }
            //获取营业执照机构名称相似度
            if (StringUtils.isNotBlank(businessLicense.getInstitutionName())) {
                licenseMatchTemp.setBusinessNameDegree(institutionNameMatch(businessLicense.getInstitutionName(), licenceInfoEntity.getObjectName(), licenceInfoEntity.getAliasName(), licenceInfoEntity.getBusinessObjectName()));
            }
            //获取营业执照地址相似度
            licenseMatchTemp.setBusinessAddressDegree(getMaxInstitutionAddressDegree(businessLicense.getAddress(), licenceInfoEntity.getAddress(), licenceInfoEntity.getBusinessAddress(), licenceInfoEntity.getPripid(), businessLicense.getLatitude(), businessLicense.getLongitude()));
        }
        if (practiceLicense != null) {
            //执业许可证登记号匹配度
            if (StringUtils.isNotBlank(licenceInfoEntity.getDocNo())) {
                licenseMatchTemp.setRegNoDegree(AlgorithmUtils.getSimilarityByLevenshteinDistance(practiceLicense.getRegisterNo(), licenceInfoEntity.getDocNo()));
            }
            //执业许可证名称匹配度
            licenseMatchTemp.setPracticeNameDegree(institutionNameMatch(practiceLicense.getInstitutionName(), licenceInfoEntity.getObjectName(), licenceInfoEntity.getAliasName(), licenceInfoEntity.getBusinessObjectName()));
            //执业许可证地址匹配度
            licenseMatchTemp.setPracticeAddressDegree(getMaxInstitutionAddressDegree(practiceLicense.getAddress(), licenceInfoEntity.getAddress(), licenceInfoEntity.getBusinessAddress(), licenceInfoEntity.getPripid(), practiceLicense.getLatitude(), practiceLicense.getLongitude()));
        }
    }

    /**
     * ocr识别地址分别与营业执照和执业许可证地址匹配，取匹配度较大的值
     * @param ocrAddress
     * @param practiceAddress
     * @param businessAddress
     * @return
     */
    private static BigDecimal getMaxInstitutionAddressDegree(String ocrAddress, String practiceAddress, String businessAddress, String priPid, BigDecimal fromLatitude, BigDecimal fromLongitude) {
        //获取ocr地址与执业许可证地址的匹配度
        float addressDegree = institutionAddressMatch(ocrAddress, practiceAddress, priPid, fromLatitude, fromLongitude);
        //获取ocr地址与营业执照地址的匹配度
        float businessAddressDegree = institutionAddressMatch(ocrAddress, businessAddress, priPid, fromLatitude, fromLongitude);
        if (addressDegree < businessAddressDegree) {
            addressDegree = businessAddressDegree;
        }
        return BigDecimal.valueOf(addressDegree);
    }

    /**
     * 获取地址匹配度
     * @param fromAddress ocr识别的证照地址
     * @param toAddress 主体地址
     * @return
     */
    private static float institutionAddressMatch(String fromAddress, String toAddress, String priPid, BigDecimal fromLatitude, BigDecimal fromLongitude) {
        if (StringUtils.isBlank(fromAddress) ||  StringUtils.isBlank(toAddress)) {
            return 0;
        }
        //如果经度或者纬度有一个为空，则认为匹配度为0
        if (fromLatitude == null || fromLongitude == null) {
            return 0;
        }
        BigDecimal toLatitude = LATITUDE_MAP.get(priPid);
        BigDecimal toLongitude = LONGITUDE_MAP.get(priPid);
        if (toLatitude == null || toLongitude == null) {
            return 0;
        }
        if (fromAddress.equals(toAddress)) {
            return 1;
        }

        if (fromAddress.contains(toAddress) || toAddress.contains(fromAddress)) {
            return 0.9f;
        }
        //先用经纬度进行匹配，如果经纬度距离大于300米，再用minHash进行匹配补偿
        double distance = LatitudeUtils.getDistanceMeterByLatitudeLongitude(fromLatitude.doubleValue(), fromLongitude.doubleValue(), toLatitude.doubleValue(), toLongitude.doubleValue());
        if (distance < 50.0d) {
            return 1;
        } else if (distance < 100.0d) {
            return 0.7f;
        } else if (distance < 300.0d) {
            return 0.4f;
        } else if (AlgorithmUtils.getSimilarityByMinHash(fromAddress, toAddress) > 0.8) {
            return 0.3f;
        }
        return 0;
    }

    /**
     * 计算机构名称匹配度,ocr名称需与营业执照名称和执业许可证名称分别进行匹配，取最大值
     * @param ocrName 营业执照在ocr上的名称
     * @param objectName 执业许可主体名称
     * @param aliasName 执业许可别名
     * @param businessName 营业执照名称
     * @return
     */
    private static BigDecimal institutionNameMatch(String ocrName, String objectName, String aliasName, String businessName) {
        if (StringUtils.isBlank(ocrName)) {
            return BigDecimal.valueOf(0);
        }
        //判断证照机构名称是否以分号分隔
        float twoNameDegree = getDegreeOfTwoName(ocrName, objectName, aliasName, businessName, DELIMITER);
        //计算ocr名称没有去除“有限公司”之前和机构名称的匹配度
        float nameDegree = AlgorithmUtils.getSimilarityByMinHash(ocrName, objectName);
        //计算ocr名称没有去除“有限公司”之前和机构别名的匹配度
        nameDegree = getMaxNameDegree(nameDegree, ocrName, aliasName);
        //计算ocr名称没有去除“有限公司”之前和营业执照机构名称的匹配度
        nameDegree = getMaxNameDegree(nameDegree, ocrName, businessName);
        if (ocrName.endsWith(INSTITUTION_NAME_SUFFIX)) {
            String replaceOcrName = ocrName.substring(0, ocrName.lastIndexOf(INSTITUTION_NAME_SUFFIX));
            //计算ocr名称去除“有限公司”之后和机构名称的匹配度
            nameDegree = getMaxNameDegree(nameDegree, replaceOcrName, objectName);
            //计算ocr名称去除“有限公司”之后和别名的匹配度
            nameDegree = getMaxNameDegree(nameDegree, replaceOcrName, aliasName);
            //计算ocr名称去除“有限公司”之后和营业执照名称的匹配度
            nameDegree = getMaxNameDegree(nameDegree, replaceOcrName, businessName);
        }
        if (ocrName.endsWith(HE_HUO_QI_YE_NORMAL)) {
            String partnershipName = ocrName.substring(0, ocrName.lastIndexOf(HE_HUO_QI_YE_NORMAL));
            //计算ocr名称去除“有限公司”之后和机构名称的匹配度
            nameDegree = getMaxNameDegree(nameDegree, partnershipName, objectName);
            //计算ocr名称去除“有限公司”之后和别名的匹配度
            nameDegree = getMaxNameDegree(nameDegree, partnershipName, aliasName);
            //计算ocr名称去除“有限公司”之后和营业执照名称的匹配度
            nameDegree = getMaxNameDegree(nameDegree, partnershipName, businessName);
        }
        if (ocrName.endsWith(HE_HUO_QI_YE)) {
            String generalPartnershipName = ocrName.substring(0, ocrName.lastIndexOf(HE_HUO_QI_YE));
            //计算ocr名称去除“有限公司”之后和机构名称的匹配度
            nameDegree = getMaxNameDegree(nameDegree, generalPartnershipName, objectName);
            //计算ocr名称去除“有限公司”之后和别名的匹配度
            nameDegree = getMaxNameDegree(nameDegree, generalPartnershipName, aliasName);
            //计算ocr名称去除“有限公司”之后和营业执照名称的匹配度
            nameDegree = getMaxNameDegree(nameDegree, generalPartnershipName, businessName);
        }
        if (nameDegree < twoNameDegree ) {
            nameDegree = twoNameDegree;
        }
        return BigDecimal.valueOf(nameDegree);
    }


    private static float getDegreeOfTwoName(String ocrName, String objectName, String aliasName, String businessName, String delimiter) {
        float nameDegree = 0;
        int firstIndex = ocrName.indexOf(delimiter);
        int lastIndex = ocrName.indexOf(delimiter);
        if (firstIndex != lastIndex || (firstIndex > 0 && !ocrName.endsWith(delimiter))) {
            int separationPoint = firstIndex + delimiter.length();
            //第一名称
            String firstName = ocrName.substring(0, separationPoint);
            //第二名称
            String secondName = ocrName.substring(separationPoint);
            //获取第一名称与执业许可证机构名称的匹配度
            nameDegree = AlgorithmUtils.getSimilarityByMinHash(firstName, objectName);
            //与第二名称和执业许可证机构名称的匹配度进行比较，取较大值
            nameDegree = getMaxNameDegree(nameDegree, secondName, objectName);
            //与第一名称与执业许可证别名的匹配度比较，取最大值
            nameDegree = getMaxNameDegree(nameDegree, firstName, aliasName);
            //与第二名称与别名的匹配度比较，取最大值
            nameDegree = getMaxNameDegree(nameDegree, secondName, aliasName);
            //与第一名称与营业执照名称匹配度比较，取最大值
            nameDegree = getMaxNameDegree(nameDegree, firstName, businessName);
            //如果名称以有限公司结尾，则需要去除有限公司再次进行匹配
            if (firstName.endsWith(INSTITUTION_NAME_SUFFIX)) {
                String firstNameReplace = firstName.replaceAll(INSTITUTION_NAME_SUFFIX, "");
                nameDegree = getMaxNameDegree(nameDegree, firstNameReplace, objectName);
                nameDegree = getMaxNameDegree(nameDegree, firstNameReplace, aliasName);
                nameDegree = getMaxNameDegree(nameDegree, firstNameReplace, businessName);
            }
            if (secondName.endsWith(INSTITUTION_NAME_SUFFIX)) {
                String secondNameReplace = secondName.replaceAll(INSTITUTION_NAME_SUFFIX, "");
                nameDegree = getMaxNameDegree(nameDegree, secondNameReplace, objectName);
                nameDegree = getMaxNameDegree(nameDegree, secondNameReplace, aliasName);
                nameDegree = getMaxNameDegree(nameDegree, secondNameReplace, businessName);
            }
        }
        return nameDegree;
    }

    /**
     * 获取名称匹配度的较大值
     * @param nameDegree
     * @param fromName
     * @param toName
     * @return
     */
    private static float getMaxNameDegree(float nameDegree, String fromName, String toName) {
        float newNameDegree = AlgorithmUtils.getSimilarityByMinHash(fromName, toName);
        if (nameDegree < newNameDegree) {
            nameDegree = newNameDegree;
        }
        return nameDegree;
    }
    
}
